﻿package me.zhengjie.common.utils

import com.fasterxml.jackson.annotation.JsonAutoDetect
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.annotation.PropertyAccessor
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator
import com.fasterxml.jackson.databind.node.ArrayNode
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import me.zhengjie.logging.slf4j.*
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat


/**
 * @Description 由于习惯了用fastjson处理JSON数据，突然改成用jackson，有些不适应，所以打算用jackson封装出类似fastjson里的方法进行使用
 * @ClassName JsonUtil
 * @Author yuhuofei
 * @Date 2023/8/19 14:36
 * @Version 1.0
 */
object JacksonUtil {
    private val objectMapper = ObjectMapper()

    // 时间日期格式
    private const val STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"

    //以静态代码块初始化
    init {
        ObjectMapper().apply {
            setSerializationInclusion(JsonInclude.Include.NON_NULL)// 不为 null 才序列化
            configure(SerializationFeature.INDENT_OUTPUT, true) // 美化格式
            setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) // 可见性，只序列化任意修饰符的字段
            disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // 关闭某个特征
            //setDateFormat(SimpleDateFormat("yyyy-MM-dd"))
            registerKotlinModule() // 注册 Kotlin 模块，支持 Kotlin 数据类
            activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,//  验证器，用于验证实际要反序列化的子类型是否有效
                ObjectMapper.DefaultTyping.NON_FINAL, // 定义哪些类型的对象需要添加额外的类型信息，NON_FINAL：非 final类都会包含
                JsonTypeInfo.As.PROPERTY // 类型信息的包含方式 PROPERTY：类型信息作为 JSON 对象的一个属性
                //JsonTypeInfo.As.WRAPPER_OBJECT
            )
        }
//        //对象的所有字段全部列入序列化
//        objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS)
//        //取消默认转换timestamps形式
//        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
//        //忽略空Bean转json的错误
//        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
//        //所有的日期格式都统一为以下的格式，即yyyy-MM-dd HH:mm:ss
//        objectMapper.setDateFormat(SimpleDateFormat(STANDARD_FORMAT))
//        //忽略 在json字符串中存在，但在java对象中不存在对应属性的情况。防止错误
//        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    }

    /**===========================以下是从JSON中获取对象==================================== */

    fun <T> parseObject(jsonString: String?, clazz: Class<T>): T? {
        var t: T? = null
        try {
            t = objectMapper.readValue(jsonString!!, clazz)
        } catch (e: JsonProcessingException) {
            logError("JsonString转为自定义对象失败：{${e.message}}")
        }
        return t
    }

    fun <T> parseObject(file: File?, `object`: Class<T>?): T? {
        var t: T? = null
        try {
            t = objectMapper.readValue(file, `object`)
        } catch (e: IOException) {
            logError("从文件中读取json字符串转为自定义对象失败：{${e.message}}")
        }
        return t
    }

    //将json数组字符串转为指定对象List列表或者Map集合
    fun <T> parseJSONArray(jsonArray: String?, reference: TypeReference<T>?): T? {
        var t: T? = null
        try {
            t = objectMapper.readValue(jsonArray, reference)
        } catch (e: JsonProcessingException) {
            logError("JSONArray转为List列表或者Map集合失败：{${e.message}}")
        }
        return t
    }

    /**=================================以下是将对象转为JSON===================================== */
    fun toJSONString(`object`: Any?): String? {
        var jsonString: String? = null
        try {
            jsonString = objectMapper.writeValueAsString(`object`)
        } catch (e: JsonProcessingException) {
            logError("Object转JSONString失败：{${e.message}}")
        }
        return jsonString
    }

    fun toByteArray(`object`: Any?): ByteArray? {
        var bytes: ByteArray? = null
        try {
            bytes = objectMapper.writeValueAsBytes(`object`)
        } catch (e: JsonProcessingException) {
            logError("Object转ByteArray失败：{${e.message}}")
        }
        return bytes
    }

    fun objectToFile(file: File?, `object`: Any?) {
        try {
            objectMapper.writeValue(file, `object`)
        } catch (e: JsonProcessingException) {
           logError("Object写入文件失败：{${e.message}}")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    /**=============================以下是与JsonNode相关的======================================= */ //JsonNode和JSONObject一样，都是JSON树形模型，只不过在jackson中，存在的是JsonNode
    fun parseJSONObject(jsonString: String?): JsonNode? {
        var jsonNode: JsonNode? = null
        try {
            jsonNode = objectMapper.readTree(jsonString)
        } catch (e: JsonProcessingException) {
            logError("JSONString转为JsonNode失败：{${e.message}}")
        }
        return jsonNode
    }

    fun parseJSONObject(`object`: Any?): JsonNode {
        return objectMapper.valueToTree(`object`)
    }

    fun toJSONString(jsonNode: JsonNode?): String? {
        var jsonString: String? = null
        try {
            jsonString = objectMapper.writeValueAsString(jsonNode)
        } catch (e: JsonProcessingException) {
            logError("JsonNode转JSONString失败：{${e.message}}")
        }
        return jsonString
    }

    //JsonNode是一个抽象类，不能实例化，创建JSON树形模型，得用JsonNode的子类ObjectNode，用法和JSONObject大同小异
    fun newJSONObject(): ObjectNode {
        return objectMapper.createObjectNode()
    }

    //创建JSON数组对象，就像JSONArray一样用
    fun newJSONArray(): ArrayNode {
        return objectMapper.createArrayNode()
    }

    /**===========以下是从JsonNode对象中获取key值的方法，个人觉得有点多余，直接用JsonNode自带的取值方法会好点，出于纠结症，还是补充进来了 */
    fun getString(jsonObject: JsonNode, key: String?): String {
        return jsonObject[key].asText()
    }

    fun getInteger(jsonObject: JsonNode, key: String?): Int {
        return jsonObject[key].asInt()
    }

    fun getBoolean(jsonObject: JsonNode, key: String?): Boolean {
        return jsonObject[key].asBoolean()
    }

    fun getJSONObject(
        jsonObject: JsonNode,
        key: String?
    ): JsonNode {
        return jsonObject[key]
    }
}

